#include <thread>
#include <logger.h>
#include <napi_call.h>
#include <node_api.h>
#include <unistd.h>

void PrintMem()
{
    std::stringstream ss;
    ss << "grep VmRSS /proc/" << getpid() << "/status";
    auto fp = popen(ss.str().c_str(), "r");
    char memoryUsage[64] = {};
    auto size = fread(memoryUsage, 1, sizeof(memoryUsage), fp);
    memoryUsage[size - 1] = 0;
    VALUE_LOGGER(memoryUsage);
    pclose(fp);
}

napi_value TestObj(napi_env env, napi_callback_info info)
{
    int number;
    size_t argc = 1;
    napi_value numberVal = nullptr;
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, &numberVal, nullptr, nullptr));
    NAPI_CALL(env, napi_get_value_int32(env, numberVal, &number));

    PrintMem();
    LOG2SO(DEBUG) << number;
    napi_value array;
    NAPI_CALL(env, napi_create_array_with_length(env, number, &array));
    for (int i = 0; i < number; i++) {
        napi_value obj;
        NAPI_CALL(env, napi_create_object(env, &obj));
        NAPI_CALL(env, napi_set_element(env, array, i, obj));
    }
    PrintMem();
    return array;
}

napi_value TestObjScope(napi_env env, napi_callback_info info)
{
    int number;
    size_t argc = 1;
    napi_value numberVal = nullptr;
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, &numberVal, nullptr, nullptr));
    NAPI_CALL(env, napi_get_value_int32(env, numberVal, &number));

    PrintMem();
    LOG2SO(DEBUG) << number;
    napi_value array;
    NAPI_CALL(env, napi_create_array_with_length(env, number, &array));
    for (int i = 0; i < number; i++) {
        napi_handle_scope scope = nullptr;
        NAPI_CALL(env, napi_open_handle_scope(env, &scope));
        napi_value obj;
        NAPI_CALL(env, napi_create_object(env, &obj));
        NAPI_CALL(env, napi_set_element(env, array, i, obj));
        NAPI_CALL(env, napi_close_handle_scope(env, scope));
    }
    PrintMem();
    return array;
}

napi_value TestObjEscapeScope(napi_env env, napi_callback_info info)
{
    napi_escapable_handle_scope scope = nullptr;
    NAPI_CALL(env, napi_open_escapable_handle_scope(env, &scope));
    napi_value object = nullptr;
    NAPI_CALL(env, napi_create_object(env, &object));
    NAPI_CALL(env, napi_escape_handle(env, scope, object, &object));
    NAPI_CALL(env, napi_close_escapable_handle_scope(env, scope));
    return object;
}

/* napi_value */
NAPI_MODULE_INIT(/* napi_env env, napi_value exports */)
{
    napi_property_descriptor props[] = {
        {.utf8name = "test_obj", .method = TestObj},
        {.utf8name = "test_obj_scope", .method = TestObjScope},
        {.utf8name = "test_obj_escape_scope", .method = TestObjEscapeScope},
    };
    for (auto &prop : props) {
        constexpr auto methodAttrs = napi_writable | napi_enumerable | napi_configurable;
        prop.attributes = static_cast<napi_property_attributes>(methodAttrs);
    }
    NAPI_CALL(env, napi_define_properties(env, exports, sizeof(props) / sizeof(*props), props));
    return exports;
}
